﻿using System;
using System.Collections.Generic;

using System.Text;
using System.Windows.Input;
using System.Threading;
using System.Windows.Threading;
using System.Windows;
using OSC.NET;
using System.Collections;
using System.Diagnostics;

namespace Bixas.Tangibilis.Input
{
    public sealed class TouchSource : DispatcherObject, IDisposable
    {
        private static TouchSource _instance;
        private TouchDevice _inputFilter;
        private Window _currentWindow;

        private OSCReceiver _receiver;
        private Thread _listenThread;
        private bool _listening;
        private int _currentFrame;
        private int _lastFrame;

        public Dictionary<int, TouchCursor> _aliveCursorList = new Dictionary<int, TouchCursor>();

        private TouchSource()
        {
            _inputFilter = new TouchDevice(this);
            InputManager.Current.PreProcessInput += new PreProcessInputEventHandler(_inputFilter.PreProcessInput);
            InputManager.Current.PostProcessInput += new ProcessInputEventHandler(_inputFilter.PostProcessInput);

            connect();
        }

        ~TouchSource()
        {
            this.Dispose();
        }

        delegate void CursorOperationDelegate(RawTouchCursorInputEventArgs e);


        public void connect()
        {
            _receiver = new OSCReceiver(3333);
            _receiver.Connect();
            _listening = true;
            _listenThread = new Thread(new ThreadStart(Listen));
            //_listenThread = Thread.AllocateDataSlot();
            _listenThread.Start();
        }

        public void disconnect()
        {
            _receiver.Close();
        }

        DispatcherFrame _frame;

        private void Listen()
        {
            while (_listening)
            {


                OSCPacket packet = _receiver.Receive();
                if (packet != null)
                {
                    if (packet.IsBundle())
                    {
                        ArrayList messages = packet.Values;
                        for (int i = 0; i < messages.Count; i++)
                        {
                            this.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Input, new ProcessMessageDelegate(processMessage), (OSCMessage)messages[i]);
                            //this.processMessage((OSCMessage)messages[i]);
                        }
                    }
                    else
                    {
                        this.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Input, new ProcessMessageDelegate(processMessage), (OSCMessage)packet);
                        //this.processMessage((OSCMessage)packet);
                    }
                }
            }
        }

        public delegate void ProcessMessageDelegate(OSCMessage message);

        private void processMessage(OSCMessage message)
        {
            string address = message.Address;
            string command = (string)message.Values[0];
            switch (address)
            {
                case "/tuio/2Dcur":
                    {
                        switch (command)
                        {
                            case "set":

                                if (_currentFrame >= _lastFrame)
                                {

                                    if (!_aliveCursorList.ContainsKey((int)message.Values[1]))
                                    {
                                        TouchCursor cursor = new TouchCursor();
                                        cursor.cursorID = (int)message.Values[1];
                                        _aliveCursorList.Add((int)message.Values[1], cursor);
                                    }

                                    TouchCursor newcursor = _aliveCursorList[(int)message.Values[1]];
                                    newcursor.OldLeft = newcursor.Left;
                                    newcursor.OldTop = newcursor.Top;
                                    newcursor.Left = (int)((float)message.Values[2] * this._currentWindow.ActualWidth);
                                    newcursor.Top = (int)((float)message.Values[3] * this._currentWindow.ActualHeight);
                                    //Point p = new Point((float)message.Values[2] * System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width, (float)message.Values[3] * System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height);
                                    //p = this.CurrentWindow.PointFromScreen(p);
                                    //newcursor.Top = p.Y;
                                    //newcursor.Left = p.X;
                                    if (!newcursor.IsUpdateFirst)
                                    {
                                        ProcessCursorOperation(new RawTouchCursorInputEventArgs(newcursor, Environment.TickCount, this._currentWindow, RawTouchCursorOperation.Add));

                                        newcursor.IsUpdateFirst = true;
                                    }
                                    else
                                    {
                                        TouchCursor cursor = _aliveCursorList[(int)message.Values[1]];
                                        if (newcursor.Left != cursor.OldLeft || newcursor.Top != cursor.OldTop)
                                            ProcessCursorOperation(new RawTouchCursorInputEventArgs(cursor, Environment.TickCount, this._currentWindow, RawTouchCursorOperation.Update));
                                    }
                                    _aliveCursorList[(int)message.Values[1]] = newcursor;
                                }
                                break;
                            case "alive":
                                if (_currentFrame >= _lastFrame)
                                {
                                    Dictionary<int, TouchCursor> aliveCursorList = new Dictionary<int, TouchCursor>();
                                    for (int i = 1; i < message.Values.Count; i++)
                                    {
                                        int cursorId = (int)message.Values[i];

                                        if (_aliveCursorList.ContainsKey(cursorId))
                                        {
                                            aliveCursorList.Add(cursorId, _aliveCursorList[cursorId]);
                                            _aliveCursorList.Remove(cursorId);
                                        }
                                        else
                                        {
                                            TouchCursor cursor = new TouchCursor();
                                            cursor.cursorID = cursorId;
                                            aliveCursorList.Add(cursorId, cursor);
                                        }

                                    }

                                    foreach (KeyValuePair<int, TouchCursor> cursor in _aliveCursorList)
                                    {
                                        ProcessCursorOperation(new RawTouchCursorInputEventArgs(cursor.Value, Environment.TickCount, this._currentWindow, RawTouchCursorOperation.Delete));
                                        cursor.Value.Dispose();
                                    }

                                    this._aliveCursorList = aliveCursorList;
                                }


                                break;
                            case "fseq":
                                UpdateFrame((int)message.Values[1]);
                                break;
                        }
                        break;
                    }
            }
        }

        private void ProcessCursorOperation(RawTouchCursorInputEventArgs e)
        {
            InputManager.Current.ProcessInput(e);
        }

        public static TouchSource getInstance()
        {
            if (TouchSource._instance == null)
                TouchSource._instance = new TouchSource();

            return TouchSource._instance;
        }

        private void UpdateFrame(int Frame)
        {
            _lastFrame = _currentFrame;
            if (_currentFrame == -1) _currentFrame = _lastFrame;
        }

        public Window CurrentWindow { get { return _currentWindow; } set { this._currentWindow = value; } }

        #region IDisposable Members

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        private void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (_instance != null)
                {
                    _instance.disconnect();
                    _inputFilter = null;
                }
                if (this._listenThread != null)
                {
                    this._listenThread.Abort();
                    this._listenThread = null;
                }

            }
        }
        #endregion

        //protected override System.Windows.Media.CompositionTarget GetCompositionTargetCore()
        //{
        //    throw new NotImplementedException();
        //}

        //public override bool IsDisposed
        //{
        //    get { throw new NotImplementedException(); }
        //}

        //public override System.Windows.Media.Visual RootVisual
        //{
        //    get
        //    {
        //        return _currentWindow;
        //    }
        //    set
        //    {
        //        _currentWindow = (Window)value;
        //    }
        //}
    }
}
